package com.funo.logic.generate.code;

import cn.hutool.core.collection.CollectionUtil;
import com.funo.logic.constant.CodeConstants;
import com.funo.logic.constant.Modifier;
import com.funo.logic.constant.SymbolConstants;
import com.funo.logic.generate.wrapper.AnnotationClassWrapper;
import com.funo.logic.generate.wrapper.ClassWrapper;
import org.apache.commons.lang.StringUtils;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 方法体代码生成
 * @since 2024年7月23日10:32:37
 * @author cms
 */
public class MethodCodeSupport extends AbstractCodeSupport implements CodeSupport {

    private String name;
    private ClassWrapper returnType;
    private List<ParamCodeSupport> parameterTypes = new LinkedList<>();
    private List<ClassWrapper> exceptionTypes = new LinkedList<>();
    private List<Modifier> modifiers = new LinkedList<>();
    private List<String> bodyLines = new LinkedList<>();
    /**
     * 方法注解集合
     */
    private List<AnnotationClassWrapper> annotationList = new LinkedList<>();
    /**
     * 这个方法全部用到的注解
     */
    private List<AnnotationClassWrapper> globalAnnotationList = new LinkedList<>();
    private List<String> commentList = new ArrayList<>();
    // 方法体里每行调用逻辑
    private List<CodeSupport> codeSupportList = new LinkedList<>();
    @Override
    public String generateCode() {

        // 添加方法上的注解
        for (AnnotationClassWrapper annotation : annotationList) {
            codeBuffer.append(annotation.generateCode());
            addNewLine();
            addIndent();
        }

        if (isPublic(modifiers)) {
            codeBuffer.append("public ");
        }

        if (isProtected(modifiers)) {
            codeBuffer.append("protected ");
        }

        if (isPrivate(modifiers)) {
            codeBuffer.append("private ");
        }

        if (isAbstract(modifiers)) {
            codeBuffer.append("abstract ");
        }

        if (isStatic(modifiers)) {
            codeBuffer.append("static ");
        }

        if (isFinal(modifiers)) {
            codeBuffer.append("final ");
        }

        if (returnType == null) {
            codeBuffer.append("void");
        } else {
            codeBuffer.append(returnType.getName());
        }

        super.addSpace();
        codeBuffer.append(name);

        super.addOpenParen();
        codeBuffer.append(parameterTypes.stream().map(ParamCodeSupport::generateCode)
            .collect(Collectors.joining(", ")));
        super.addCloseParen();

        if (CollectionUtil.isNotEmpty(exceptionTypes)) {
            codeBuffer.append(" throws ");
            codeBuffer.append(exceptionTypes.stream().map(ClassWrapper::getShortName)
                .collect(Collectors.joining(", ")));
        }

        super.addSpace();
        super.addOpenBrace();

        int indent = CodeConstants.INDENT << 1;

        for (String bodyLine : bodyLines) {
            if (bodyLine.startsWith("}")) {
                indent -= CodeConstants.INDENT;
            }
            super.addNewLine();
            super.addIndent(indent);
            if (!SymbolConstants.LINE_BREAK.equals(bodyLine)) {
                super.addBodyLine(bodyLine);
            }

            if (bodyLine.endsWith("{") || bodyLine.endsWith(":")) {
                indent += CodeConstants.INDENT;
            }
        }

        indent -= CodeConstants.INDENT;
        super.addNewLine();
        for (CodeSupport codeSupport : codeSupportList) {
            addIndent(indent + CodeConstants.INDENT);
            codeBuffer.append(codeSupport.generateCode());
            addNewLine();
        }

        addIndent(indent + CodeConstants.INDENT);
        if (returnType != null) {
            codeBuffer.append("return ").append(returnType.getName());
            addPoint();
            codeBuffer.append("success()");
            addSemicolon();
            addNewLine();
        }

        super.addIndent(indent);
        super.addCloseBrace();

        return codeBuffer.toString();
    }

    public boolean isSameMethod(MethodCodeSupport method) {
        if (!name.equals(method.getName())) {
            return false;
        }
        if (method.getParameterTypes() == null) {
            return parameterTypes == null;
        }
        if (parameterTypes == null) {
            return method.getParameterTypes() == null;
        }
        if (method.getParameterTypes().size() != parameterTypes.size()) {
            return false;
        }
        for (int i = 0; i < method.getParameterTypes().size(); i++) {
            if (!method.getParameterTypes().get(i).getType().getName()
                .equals(parameterTypes.get(i).getType().getName())) {
                return false;
            }
        }
        return true;
    }

    public String getLastNoneBlankBodyLine() {
        for (int i = bodyLines.size() - 1; i >= 0; i--) {
            String bodyLine = bodyLines.get(i);
            if (StringUtils.isNotBlank(bodyLine)) {
                return bodyLine;
            }
        }
        return null;
    }

    private String getLastBodyLine() {
        return bodyLines.get(bodyLines.size() - 1);
    }

    @Override
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setReturnType(ClassWrapper returnType) {
        this.returnType = returnType;
    }

    public List<ParamCodeSupport> getParameterTypes() {
        return parameterTypes;
    }

    public void addModifier(Modifier modifier) {
        modifiers.add(modifier);
    }

    public void addParameter(ParamCodeSupport paramTarget) {
        parameterTypes.add(paramTarget);
    }

    public void addException(ClassWrapper exceptionType) {
        exceptionTypes.add(exceptionType);
    }

    @Override
    public void addBodyLine(String line) {
        bodyLines.add(line);
    }

    @Override
    public void addBodyLines(List<String> lines) {
        bodyLines.addAll(lines);
    }

    @Override
    public void appendLine(String line) {
        bodyLines.set(bodyLines.size() - 1, getLastBodyLine() + line);
    }

    public void addAnnotation(AnnotationClassWrapper annotation) {
        annotationList.add(annotation);
        addGlobalAnnotation(annotation);
    }

    public List<AnnotationClassWrapper> getAnnotationList() {
        return annotationList;
    }

    public void addComment(String comment) {
        commentList.add(comment);
    }

    public void addCodeSupport(CodeSupport codeSupport) {
        codeSupportList.add(codeSupport);
    }

    public void addGlobalAnnotation(AnnotationClassWrapper annotation) {
        if (globalAnnotationList.stream().noneMatch(a -> a.getFullName().equals(annotation.getFullName()))) {
            globalAnnotationList.add(annotation);
        }
    }

    public List<AnnotationClassWrapper> getGlobalAnnotationList() {
        return globalAnnotationList;
    }
}
